﻿using DoNetTool.Common.Core;
using DoNetTool.Common.Core.Cryptography;
using DoNetTool.Common.Core.Web;
using System;
using System.Security.Principal;
using System.Web;

namespace DoNetTool.Common.Web
{

    /// <summary>
    /// 网络用户
    /// </summary>
    public interface IWebUser
    {
        /// <summary>
        /// 获取用户身份
        /// </summary>
        /// <returns></returns>
        string GetRole();
    }
    /// <summary>
    /// 用户登陆身份验证
    /// </summary>
    /// <typeparam name="TUserData"></typeparam>
    public class ClientLoginPrincipal<TUserData> : IPrincipal where TUserData : class, IPrincipal, new()
    {
        private IIdentity _identity;
        private TUserData _userData;

        /// <summary>
        /// 初始化令牌和用户数据
        /// </summary>
        /// <param name="ticket"></param>
        /// <param name="userData"></param>
        public ClientLoginPrincipal(FormsAuthenticationTicket ticket, TUserData userData)
        {
            if (ticket == null)
                throw new ArgumentNullException("ticket");

            if (userData == null)
                throw new ArgumentNullException("userData");

            _identity = new FormsIdentity(ticket);
            _userData = userData;
        }

        /// <summary>
        /// 用户数据
        /// </summary>
        public TUserData UserData
        {
            get
            {
                return _userData;
            }
        }
        /// <summary>
        /// 用户身份
        /// </summary>
        public IIdentity Identity
        {
            get
            {
                return _identity;
            }
        }
        /// <summary>
        /// 验证用户权限
        /// </summary>
        /// <param name="role"></param>
        /// <returns></returns>
        public bool IsInRole(string role)
        {
            // 把判断用户组的操作留给UserData去实现。
            var principal = _userData as IPrincipal;
            if (principal == null)
                throw new NotImplementedException();
            else
                return principal.IsInRole(role);
        }




        /// <summary>
        /// 执行用户登录操作
        /// </summary>
        /// <param name="sUserKey">与登录名相关的用户信息</param>
        /// <param name="expiration">登录Cookie的过期时间，单位：分钟。</param>
        public static void SignIn(string sUserKey, int expiration, HttpContext context)
        {
            if (sUserKey == null)
                throw new ArgumentNullException("userData");

            // 第一步，将用户数据进行序列化
            string sSha1 = MySha1.CreateSha1(sUserKey);
            sUserKey = WebUserTool.GetWebUserAESEncrypt($"{sSha1}|{sUserKey}");

            // 4. 根据加密结果创建登录Cookie
            var cookie = new HttpCookie("WebUser", sUserKey);
            {
                var withBlock = cookie;
                withBlock.HttpOnly = true;
                withBlock.Secure = FormsAuthentication.RequireSSL;
                withBlock.Domain = FormsAuthentication.CookieDomain;
                withBlock.Path = FormsAuthentication.FormsCookiePath;
                if ((expiration > 0))
                    cookie.Expires = DateTime.Now.AddMinutes(expiration);
                else
                    cookie.Expires = DateTime.Now.AddMinutes(30);
            }
            if ((context == null))
                throw new InvalidOperationException();
            // 5. 写登录Cookie
            {
                var withBlock = context.Response.Cookies;
                withBlock.Remove(cookie.Name);
            }

            context.Response.AppendCookie(cookie);
        }

        /// <summary>
        ///     ''' 根据HttpContext对象设置用户标识对象
        ///     ''' </summary>
        public static void TrySetUserInfo(HttpContext context, VerifyUserKey verify)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            context.User = null;

            // 1. 读登录Cookie
            var cookie = context.Request.Cookies["WebUser"];
            if ((cookie == null))
                return;
            if (string.IsNullOrEmpty(cookie.Value))
                return;


            try
            {
                string sUserKey = WebUserTool.GetWebUserAESDecrypt(cookie.Value);
                if (string.IsNullOrEmpty(sUserKey))
                    return;

                var tmpary = sUserKey.Split('|');
                if (tmpary.Length != 2)
                    return;
                // 解密Key值
                string sSha1 = tmpary[0];
                sUserKey = tmpary[1];

                // 检查sha1 值是否相等
                var sTmpSha1 = MySha1.CreateSha1(sUserKey);
                if (!sSha1.Equals(sTmpSha1))
                    return;
                // 检查用户有效性
                IWebUser oWebUser = verify(sUserKey);
                if (oWebUser == null)
                {
                    return;
                }

                TUserData userData = new TUserData();
                var ticket = new FormsAuthenticationTicket(2, oWebUser.GetRole(), DateTime.Now, DateTime.Now.AddDays(1), false, string.Empty);
                context.User = new ClientLoginPrincipal<TUserData>(ticket, userData);

                if (userData is ClientLoginUser)
                {
                    ClientLoginUser oUser = userData as ClientLoginUser;
                    oUser.Key = sUserKey;
                    oUser.Role = oWebUser.GetRole();
                }
            }
            catch (Exception ex)
            {
                context.User = null;
            }
        }

        /// <summary>
        /// 用于验证web用户是否存在
        /// </summary>
        /// <param name="sUserKey"></param>
        /// <returns></returns>
        public delegate IWebUser VerifyUserKey(string sUserKey);

        /// <summary>
        /// 创建一个登陆凭证
        /// </summary>
        /// <param name="context"></param>
        /// <param name="Role"></param>
        public static void TrySetLoginUserInfo(HttpContext context, string Role)
        {
            if (context == null)
                throw new ArgumentNullException("context");

            try
            {
                TUserData userData = null;

                var ticket = new FormsAuthenticationTicket(2, "login", DateTime.Now, DateTime.Now.AddDays(1), false, string.Empty);
                userData = new TUserData();
                context.User = new ClientLoginPrincipal<TUserData>(ticket, userData);
                if (userData is ClientLoginUser_LoginTmp)
                {
                    ClientLoginUser_LoginTmp oUser = userData as ClientLoginUser_LoginTmp;
                    oUser.Role = Role;
                }
            }
            catch (Exception ex)
            {
                context.User = null;
            }
        }
    }

}
